use anyhow::{Context, Result};
use n0_future::StreamExt;
use shared::{ChatNode, ChatTicket, Event, NodeId, WebShareProxyInfo};
use std::sync::Arc;
use tracing::{debug, error, info, warn};

use crate::state::{
    ConnectionConfig, EventBroadcaster, RitermState, ServerMessage, TerminalSession,
};

/// Connection manager for handling iroh protocol connections
pub struct ConnectionManager {
    state: Arc<RitermState>,
    event_broadcaster: Arc<EventBroadcaster>,
}

impl ConnectionManager {
    pub fn new(state: Arc<RitermState>, event_broadcaster: Arc<EventBroadcaster>) -> Self {
        Self {
            state,
            event_broadcaster,
        }
    }

    /// Connect to a remote CLI using a ticket
    pub async fn connect(&self, config: ConnectionConfig) -> Result<()> {
        info!("Attempting to connect with ticket: {}", config.ticket);

        // Parse secret key if provided
        let secret_key = if let Some(secret_str) = config.secret_key {
            let bytes = data_encoding::HEXLOWER
                .decode(secret_str.as_bytes())
                .context("Failed to parse secret key")?;
            if bytes.len() == 32 {
                let mut array = [0u8; 32];
                array.copy_from_slice(&bytes);
                Some(shared::SecretKey::from_bytes(&array))
            } else {
                return Err(anyhow::anyhow!("Secret key must be exactly 32 bytes"));
            }
        } else {
            None
        };

        // Create chat node
        let node = ChatNode::spawn(secret_key).await?;
        let node_id = node.node_id();
        info!("Created chat node with ID: {}", node_id);

        // Parse ticket
        let ticket = ChatTicket::deserialize(&config.ticket).context("Failed to parse ticket")?;

        // Join the channel
        let nickname = config
            .nickname
            .unwrap_or_else(|| "riterm-client".to_string());
        let (_sender, mut receiver) = node
            .join(&ticket, nickname.clone())
            .await
            .context("Failed to join channel")?;

        // Update state
        let _ = self
            .state
            .update_connection_state(|state| {
                state.node_id = Some(node_id.to_string());
                state.ticket = Some(config.ticket.clone());
                state.is_connected = true;
            })
            .await;

        // Store the chat node in state
        // Note: In a proper implementation, we'd modify RitermState to store the chat node
        // For now, we'll proceed with the connection without storing the node directly

        // Broadcast connection success
        self.event_broadcaster
            .broadcast(ServerMessage::Connected {
                node_id: node_id.to_string(),
                ticket: config.ticket.clone(),
            })
            .await;

        // Start event handling loop
        let event_broadcaster = self.event_broadcaster.clone();
        let state = self.state.clone();
        let local_node_id = node_id;

        tokio::spawn(async move {
            while let Some(event_result) = receiver.next().await {
                match event_result {
                    Ok(event) => {
                        if let Err(e) =
                            Self::handle_event(event, &event_broadcaster, &state, local_node_id)
                                .await
                        {
                            error!("Error handling event: {}", e);
                        }
                    }
                    Err(e) => {
                        error!("Error receiving event: {}", e);
                        break;
                    }
                }
            }

            // Connection lost
            warn!("Event stream ended, connection lost");
            event_broadcaster
                .broadcast(ServerMessage::Disconnected)
                .await;

            // Update connection state
            state
                .update_connection_state(|state| {
                    state.is_connected = false;
                })
                .await;
        });

        info!("Successfully connected to remote CLI");
        Ok(())
    }

    /// Disconnect from the remote CLI
    pub async fn disconnect(&self) -> Result<()> {
        info!("Disconnecting from remote CLI");

        // Update state
        self.state
            .update_connection_state(|state| {
                state.is_connected = false;
                state.connected_terminals.clear();
                state.active_proxies.clear();
                state.peers.clear();
            })
            .await;

        // Broadcast disconnection
        self.event_broadcaster
            .broadcast(ServerMessage::Disconnected)
            .await;

        info!("Disconnected from remote CLI");
        Ok(())
    }

    /// Handle incoming events from the iroh network
    async fn handle_event(
        event: Event,
        event_broadcaster: &EventBroadcaster,
        state: &Arc<RitermState>,
        local_node_id: NodeId,
    ) -> Result<()> {
        match event {
            Event::NodeJoined { node_id } => {
                debug!("Node joined: {}", node_id);
                state
                    .update_connection_state(|state| {
                        if !state.peers.contains(&node_id.to_string()) {
                            state.peers.push(node_id.to_string());
                        }
                    })
                    .await;

                event_broadcaster
                    .broadcast(ServerMessage::NodeJoined {
                        node_id: node_id.to_string(),
                    })
                    .await;
            }

            Event::NodeLeft { node_id } => {
                debug!("Node left: {}", node_id);
                state
                    .update_connection_state(|state| {
                        state.peers.retain(|peer| peer != &node_id.to_string());
                    })
                    .await;

                event_broadcaster
                    .broadcast(ServerMessage::NodeLeft {
                        node_id: node_id.to_string(),
                    })
                    .await;
            }

            Event::MessageReceived {
                from,
                text,
                nickname: _,
            } => {
                debug!("Message received from {}: {}", from.fmt_short(), text);
                // Handle chat messages if needed
            }

            Event::TerminalBatch { from, operations } => {
                debug!("Received terminal batch from {}", from.fmt_short());
                // Process terminal operations
                for operation in operations {
                    Self::handle_terminal_operation(operation, event_broadcaster, state).await?;
                }
            }

            Event::TerminalState { from, terminals } => {
                debug!("Received terminal state from {}", from.fmt_short());
                let terminal_sessions: Vec<TerminalSession> = terminals
                    .clone()
                    .into_iter()
                    .map(TerminalSession::from)
                    .collect();

                state
                    .update_connection_state(|state| {
                        state.connected_terminals = terminals;
                    })
                    .await;

                event_broadcaster
                    .broadcast(ServerMessage::TerminalStateChanged {
                        terminals: terminal_sessions,
                    })
                    .await;
            }

            Event::WebShare {
                from,
                local_port,
                public_port,
            } => {
                debug!(
                    "Web share request from {}: {} -> {}",
                    from.fmt_short(),
                    local_port,
                    public_port
                );

                // Add to active proxies
                let proxy_info = WebShareProxyInfo {
                    local_port,
                    public_port,
                    node_id: from,
                    status: "active".to_string(),
                    created_at: std::time::SystemTime::now()
                        .duration_since(std::time::UNIX_EPOCH)
                        .unwrap()
                        .as_secs(),
                };

                state
                    .update_connection_state(|state| {
                        if !state
                            .active_proxies
                            .iter()
                            .any(|p| p.public_port == public_port)
                        {
                            state.active_proxies.push(proxy_info);
                        }
                    })
                    .await;

                event_broadcaster
                    .broadcast(ServerMessage::WebShareStarted {
                        local_port,
                        public_port,
                    })
                    .await;
            }

            Event::WebShareStop { from, public_port } => {
                debug!(
                    "Web share stop request from {}: port {}",
                    from.fmt_short(),
                    public_port
                );

                state
                    .update_connection_state(|state| {
                        state
                            .active_proxies
                            .retain(|p| p.public_port != public_port);
                    })
                    .await;

                event_broadcaster
                    .broadcast(ServerMessage::WebShareStopped { public_port })
                    .await;
            }

            Event::WebShareState { from, proxies } => {
                debug!("Received web share state from {}", from.fmt_short());
                state
                    .update_connection_state(|state| {
                        // Only keep proxies from other nodes
                        state.active_proxies = proxies
                            .into_iter()
                            .filter(|p| p.node_id != local_node_id)
                            .collect();
                    })
                    .await;
            }

            Event::Lagged => {
                warn!("Gossip stream lagged");
            }
        }

        Ok(())
    }

    /// Handle individual terminal operations
    async fn handle_terminal_operation(
        operation: shared::TerminalOp,
        event_broadcaster: &EventBroadcaster,
        state: &Arc<RitermState>,
    ) -> Result<()> {
        match operation {
            shared::TerminalOp::Create {
                session_id,
                shell_path: _,
                name: _,
            } => {
                info!("Terminal created: {}", session_id);
                event_broadcaster
                    .broadcast(ServerMessage::TerminalCreated { session_id })
                    .await;
            }

            shared::TerminalOp::Output { session_id, data } => {
                event_broadcaster
                    .broadcast(ServerMessage::TerminalOutput { session_id, data })
                    .await;
            }

            shared::TerminalOp::Status { session_id, status } => {
                event_broadcaster
                    .broadcast(ServerMessage::TerminalStatus {
                        session_id: session_id.clone(),
                        status: status.clone(),
                    })
                    .await;

                // Update terminal status in state
                state
                    .update_connection_state(|state| {
                        if let Some(terminal) = state
                            .connected_terminals
                            .iter_mut()
                            .find(|t| t.session_id == session_id)
                        {
                            terminal.status = status;
                        }
                    })
                    .await;
            }

            shared::TerminalOp::Close { session_id } => {
                info!("Terminal closed: {}", session_id);
                state
                    .update_connection_state(|state| {
                        state
                            .connected_terminals
                            .retain(|t| t.session_id != session_id);
                    })
                    .await;
            }

            shared::TerminalOp::Rename {
                session_id,
                new_name,
            } => {
                state
                    .update_connection_state(|state| {
                        if let Some(terminal) = state
                            .connected_terminals
                            .iter_mut()
                            .find(|t| t.session_id == session_id)
                        {
                            terminal.name = new_name;
                        }
                    })
                    .await;
            }

            shared::TerminalOp::Input { .. } | shared::TerminalOp::Resize { .. } => {
                // These operations are handled elsewhere
            }
        }

        Ok(())
    }
}
